﻿using AdminModels;
using CoinModels;
using CoinModels.BTCe;
using CoinModels.Cryptsy;
using Common;
using Entities;
using Entities.CoinEntities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataPool.Tickers
{
    public static class CoinDataPooler
    {
        private static bool ContinueOnFail = Settings.GetSetting("ContinueOnFailedCollect", false);
        private static bool PoolBTCe = Settings.GetSetting("UseBTCeForPooling", true);
        private static bool PoolCryptsy = Settings.GetSetting("UseCryptsyForPooling", true);
        private static bool CollectBTCe = Settings.GetSetting("UseBTCeForCollecting", true);
        private static bool CollectCryptsy = Settings.GetSetting("UseCryptsyForCollecting", true);
        private static List<CoinType> CoinTypes { get; set; }

        public static List<BTCeCoinBase> BTCeList { get; set; }
        public static List<CryptsyCoin> CryptsyList { get; set; }
        public static List<PoolCoin> PoolList { get; set; }


        /// <summary>
        /// Resets the static settings to current values. Used by event handling.
        /// </summary>
        private static void ResetSettings()
        {
            ContinueOnFail = Settings.GetSetting("ContinueOnFailedCollect", false);
            PoolBTCe = Settings.GetSetting("UseBTCeForPooling", true);
            PoolCryptsy = Settings.GetSetting("UseCryptsyForPooling", true);
            CollectBTCe = Settings.GetSetting("UseBTCeForCollecting", true);
            CollectCryptsy = Settings.GetSetting("UseCryptsyForCollecting", true);
        }

        /// <summary>
        /// Collects and Pools Ticker Data
        /// </summary>
        /// <returns>If Pooling was Successful</returns>
        public static bool PoolCoins()
        {
            bool suceeded = false, collected = false;
            BTCeList = null;
            CryptsyList = null;

            collected = CollectData();

            if (collected || ContinueOnFail)
            {
                suceeded = PoolData(Settings.GetSetting("UpdateCoinTypesForPooling", true));
            }
            else
            {
                ErrorLog.SaveLog(new Exception("Data Collection Failed. Not continuing per setting."), "Ticker Data Pooler", ErrorType.Pooling);
            }

            return suceeded;
        }

        /// <summary>
        /// Collects Ticker Data
        /// </summary>
        /// <returns>If Collection was Sucessful</returns>
        private static bool CollectData()
        {
            bool suceeded = true;
            List<BTCeCoinBase> btc = new List<BTCeCoinBase>();
            List<CryptsyCoin> cryptsy = new List<CryptsyCoin>();

            if (CollectBTCe)
            {
                btc = BTCeDataCollector.CollectData();
                if (btc.Count == 0)
                {
                    ErrorLog.SaveLog(new Exception("BTC failed to pool."), "TickerDataPooler", ErrorType.Pooling);
                    suceeded = false;
                }
                else
                {
                    BTCeList = btc;
                }
            }

            if (CollectCryptsy)
            {
                cryptsy = CryptsyDataCollector.CollectData();
                if (btc.Count == 0)
                {
                    ErrorLog.SaveLog(new Exception("Cryptsy failed to pool."), "TickerDataPooler", ErrorType.Pooling);
                    suceeded = false;
                }
                else
                {
                    CryptsyList = cryptsy;
                }
            }

            return suceeded;
        }

        /// <summary>
        /// Pools Ticker Data
        /// </summary>
        /// <returns>If Pooling was Sucessful</returns>
        private static bool PoolData(bool updateTypes = true)
        {
            bool suceeded = false;
            DateTime addTime = DateTime.Now;

            List<CoinType> coinTypes = GetAllCoins(updateTypes);
            List<PoolCoin> coins = new List<PoolCoin>();

            foreach (CoinType coinType in coinTypes)
            {
                PoolCoin coin = new PoolCoin();
                BTCeCoinBase btceCoin = null;
                CryptsyCoin cryptsyCoin = null;
                List<iBasicCoinValues> pool = new List<iBasicCoinValues>();

                if (PoolBTCe)
                {
                    // Grab the BTCe coin if it exists & we want to
                    btceCoin = BTCeList
                        .Where(btce => btce.primarycode == coinType.PrimaryCode && btce.secondarycode == coinType.SecondaryCode)
                        .FirstOrDefault();
                    pool.Add(btceCoin);
                }
                if (PoolCryptsy)
                {
                    // Grab the Cryptsy coin if it exists & we want to
                    cryptsyCoin = CryptsyList
                        .Where(cryptsy => cryptsy.primarycode == coinType.PrimaryCode && cryptsy.secondarycode == coinType.SecondaryCode)
                        .FirstOrDefault();
                    pool.Add(cryptsyCoin);
                }

                coin.primarycode = coinType.PrimaryCode;
                coin.secondarycode = coinType.SecondaryCode;
                coin.avg = ConversionHelpers.GetAverage(GetAvgs(pool));
                coin.low = ConversionHelpers.GetAverage(GetLows(pool));
                coin.high = ConversionHelpers.GetAverage(GetHighs(pool));
                coin.vol = ConversionHelpers.GetAverage(GetVols(pool));
                coin.Created = addTime;
                coin.StdDevAvg = ConversionHelpers.GetStandardDeviation(GetAvgs(pool));
                coin.StdDevLow = ConversionHelpers.GetStandardDeviation(GetLows(pool));
                coin.StdDevHigh = ConversionHelpers.GetStandardDeviation(GetHighs(pool));

                if (btceCoin != null)
                    coin.AddMarket(Enums.Markets.BTCe);
                if (cryptsyCoin != null)
                    coin.AddMarket(Enums.Markets.Cryptsy);

                coins.Add(coin);
            }

            try
            {
                PoolCoin.PersistCoinCollection(coins);
                suceeded = true;
                PoolList = coins;
            } catch (Exception ex)
            {
                ErrorLog.SaveLog(ex, "TickerDataPooler", ErrorType.Persistance);
                throw;
            }

            return suceeded;
        }

        /// <summary>
        /// Gets a list of the averages for the coins provided
        /// </summary>
        /// <param name="coins">Coins to find averages</param>
        /// <returns>Averages (Default empty list)</returns>
        private static List<double?> GetAvgs(List<iBasicCoinValues> coins)
        {
            List<double?> Avgs = new List<double?>();

            foreach (iBasicCoinValues coin in coins)
            {
                if (coin != null)
                    Avgs.Add(coin.avg);
            }

            return Avgs;
        }

        /// <summary>
        /// Gets a list of the highs for the coins provided
        /// </summary>
        /// <param name="coins">Coins to find highs</param>
        /// <returns>highs (Default empty list)</returns>
        private static List<double?> GetHighs(List<iBasicCoinValues> coins)
        {
            List<double?> Highs = new List<double?>();

            foreach (iBasicCoinValues coin in coins)
            {
                if (coin != null)
                    Highs.Add(coin.high);
            }

            return Highs;
        }

        /// <summary>
        /// Gets a list of the Lows for the coins provided
        /// </summary>
        /// <param name="coins">Coins to find Lows</param>
        /// <returns>Lows (Default empty list)</returns>
        private static List<double?> GetLows(List<iBasicCoinValues> coins)
        {
            List<double?> Lows = new List<double?>();

            foreach (iBasicCoinValues coin in coins)
            {
                if (coin != null)
                    Lows.Add(coin.low);
            }

            return Lows;
        }

        /// <summary>
        /// Gets a list of the volumes for the coins provided
        /// </summary>
        /// <param name="coins">Coins to find volumes</param>
        /// <returns>volumes (Default empty list)</returns>
        private static List<double?> GetVols(List<iBasicCoinValues> coins)
        {
            List<double?> Vols = new List<double?>();

            foreach (iBasicCoinValues coin in coins)
            {
                if (coin != null)
                    Vols.Add(coin.vol);
            }

            return Vols;
        }

        /// <summary>
        /// Gets a list of primary/secondary names of all coins stored
        /// </summary>
        /// <returns>List of primary/secondary names of all coins stored</returns>
        private static List<CoinType> GetAllCoins(bool updateTypes = true)
        {
            List<CoinType> coinsToGrab = new List<CoinType>();

            if (updateTypes)
            {
                if (CoinTypes == null || CoinTypes.Count == 0)
                {
                    CoinTypes = CoinType.GetAllCoinTypes();
                }

                coinsToGrab = CoinTypes;
            }
            else
            {
                coinsToGrab = CoinType.GetAllCoinTypes();
            }

            return coinsToGrab;
        }

        /// <summary>
        /// Gets list of most recently collected Coins
        /// </summary>
        /// <returns>List of most recently collected coins</returns>
        public static List<PoolCoin> GetMostRecentPoolCoins()
        {
            List<PoolCoin> coins = new List<PoolCoin>();
            PoolCoinEntity[] tempList;

            using (AegirContext db = new AegirContext())
            {
                DateTime mostRecentDate = db.Pool_Coin.Max(x => x.created);
                ErrorLog.SaveLog(new Exception("DateTime Found Pool" + mostRecentDate));
                tempList = db.Pool_Coin.Where(x => x.created == mostRecentDate).ToArray();
            }

            foreach (var entity in tempList)
            {
                coins.Add(PoolCoin.ConvertToModel(entity));
            }

            return coins;
        }
    }
}
